#include "FeedbackTranslation.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;

const float CIView::FeedbackTranslation::ARROWHEAD_RADIUS = 2.7f;

/// <summary>
/// Constructor
/// </summary>
CIView::FeedbackTranslation::FeedbackTranslation() : quadricObj (0), x (false), y (false), z (false) {

    centralBox = Box(Vertex (-ARROWHEAD_RADIUS,
                             -ARROWHEAD_RADIUS,
                             -ARROWHEAD_RADIUS),                            

                     Vertex (ARROWHEAD_RADIUS,
                             ARROWHEAD_RADIUS,
                             ARROWHEAD_RADIUS));

    quadricObj = gluNewQuadric();   //Creates a quadric object
    
    x_axisBoundingBox = Box (Vertex (0,-ARROWHEAD_RADIUS, -ARROWHEAD_RADIUS), 
                             Vertex (ARROWHEAD_RADIUS*11, ARROWHEAD_RADIUS, ARROWHEAD_RADIUS));
    
    y_axisBoundingBox = Box (Vertex (-ARROWHEAD_RADIUS, 0, -ARROWHEAD_RADIUS), 
                             Vertex (ARROWHEAD_RADIUS, ARROWHEAD_RADIUS*11, ARROWHEAD_RADIUS));
    
    z_axisBoundingBox = Box (Vertex (-ARROWHEAD_RADIUS, -ARROWHEAD_RADIUS,0), 
                             Vertex (ARROWHEAD_RADIUS, ARROWHEAD_RADIUS, ARROWHEAD_RADIUS*11));
}

/// <summary>
/// Destructor
/// </summary>
CIView::FeedbackTranslation::~FeedbackTranslation() {
    //glDeleteLists (startList, 1);           //Deletes the dispaly list
    gluDeleteQuadric (quadricObj);          //Deletes the OpenGL quadric object
}







///<************************************************************************************************************>
///<*********************************** region Protected Utility Methods ***************************************>
///<************************************************************************************************************>

/// <summary>
/// Method called to draw the "ghost" feedback while necessary
/// </summary>
/// <param name="camera"> A reference to the current camera </param>
void CIView::FeedbackTranslation::DrawGhostFeedback (Camera& camera) {
    double scaleValue = 1.0 / camera.zoom;         

    //Drawing the "ghost" feedback
    glPushMatrix();                 
        glTranslatef (ghostPosition.x, ghostPosition.y, ghostPosition.z);
        glScaled (scaleValue, scaleValue, scaleValue);
        
        //Enabling blending for transparency
        glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable (GL_BLEND);                                //Enables blending

        glColor4f (1, 1, 1, 0.4f);

        //The central box  
        centralBox.Render();

        //TODO -> Use materials instead of glColor!

        if (camera.type != Camera::RIGHT) {
            //x-axis
            glPushMatrix();
                glRotatef (90, 0, 1, 0);  
                DrawArrow();                      //Draws the arrow
            glPopMatrix();
        }

        if (camera.type != Camera::TOP) {
            //y-axis
            glPushMatrix();
                glRotatef (-90, 1, 0, 0);            
                DrawArrow();                      //Draws the arrow
            glPopMatrix();
        }

        if (camera.type != Camera::FRONT) {
            //z-axis
            DrawArrow();                          //Draws the arrow                 
        }

        glDisable (GL_BLEND);                               //Disables blending
    glPopMatrix();
}

void CIView::FeedbackTranslation::DrawArrow() {
    glLineWidth (1.0f);

    glBegin (GL_LINES);                                         //The axis
        glVertex3f (0, 0, 0);
        glVertex3f (0, 0, ARROWHEAD_RADIUS*8);
    glEnd();

    glTranslatef (0, 0, ARROWHEAD_RADIUS*8);
    gluDisk (quadricObj, 0, ARROWHEAD_RADIUS, 10, 1);                               //The arrowhead base
    gluCylinder (quadricObj, ARROWHEAD_RADIUS, 0, ARROWHEAD_RADIUS*3, 10.0, 10.0);  //The arrowhead (a cone)
}
/***************************************************************************************************************
************************************** endregion Protected Utility Methods *************************************
****************************************************************************************************************/










///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary>
/// Method called to compute the feedback position
/// </summary>
/// <param name="scene"> A reference to the 3D scene </param>
void CIView::FeedbackTranslation::ComputeFeedbackPosition (Scene& scene) {
    map<const char*, Geometry*>& geometries = scene.GetGeometries();
    map<const char*, Geometry*>::iterator it = geometries.begin();
    renderizable = false;
    
    DeselectAll();  //Deselects all feedback axes
    Box box;        //The bounding box of the selected vertices

    //Checking selected vertices
    while (it != geometries.end()) {
        VertexLib& vertices = it->second->GetVertices();
        VertexIter v = vertices.begin();

        if (it->second->hasPrimitivesSelected) {

            while (v != vertices.end()) {
                if ((*v)->selected) {           
                    renderizable = true;        //Since there is a vertex selected, the feedback can be rendered
                    box.Update (*(*v));         //Updates the bounding box of the selected vertices
                }
                v++;
            }
        }

        it++;
    }
    position = box.GetCentre();             //Computes the centre of the bounding box
}

/// <summary>
/// Method called to draw the translation feedback
/// </summary>
/// <param name="camera"> A reference to the current camera </param>
void CIView::FeedbackTranslation::Render (Camera& camera) {
    if( !renderizable)      //If the feedback can't be rendered (for example, because there are no primitives selected)
        return;             //must return    

    GLint oldModel;
    glGetIntegerv (GL_SHADE_MODEL, &oldModel);      //Gets the current shade model
    glShadeModel (GL_SMOOTH);                       //Sets the smooth shade model

    //Drawing the "ghost" feedback (only if at least one axis is selected)
    if (x || y || z)
        DrawGhostFeedback (camera);

    double scaleValue = 1.0 / camera.zoom;

    glPushMatrix();                 
        glTranslatef (position.x, position.y, position.z);
        glScaled (scaleValue, scaleValue, scaleValue);
                
        //The central box  
        glColor3f (1, 1, 0);
        centralBox.Render();

        //TODO -> Use materials instead of glColor!

        if (camera.type != Camera::RIGHT) {
            //x-axis
            glPushMatrix();
                x ? glColor3f (1, 1, 0) : glColor3f (1, 0, 0);  //The x-axis color according to its selection status

                glRotatef (90, 0, 1, 0);  
                DrawArrow();                          //Draws the arrow
            glPopMatrix();
        }

        if (camera.type != Camera::TOP) {
            //y-axis
            glPushMatrix();
                y ? glColor3f (1, 1, 0) : glColor3f (0, 1, 0);  //The y-axis color according to its selection status

                glRotatef (-90, 1, 0, 0);            
                DrawArrow();                          //Draws the arrow
            glPopMatrix();
        }

        if (camera.type != Camera::FRONT) {
            //z-axis
            glPushMatrix();
                z ? glColor3f (1, 1, 0) : glColor3f (0, 0, 1);  //The z-axis color according to its selection status

                DrawArrow();                          //Draws the arrow 
            glPopMatrix();
        }

    glPopMatrix();

    glShadeModel (oldModel);                //Restores the old shade model
}

/// <summary>
/// Method called to load the names for the axes in the OpenGL names stack       
/// </summary>
/// <param name="camera"> A reference to the current camera </param>
void CIView::FeedbackTranslation::LoadAxesNames (Camera& camera) {
    if( !renderizable)      //If the feedback can't be rendered (for example, because there are no primitives selected)
        return;             //must return

    double scaleValue = 1.0 / camera.zoom;

    glPushMatrix();                 
        glTranslatef (position.x, position.y, position.z);
        glScaled (scaleValue, scaleValue, scaleValue);

        glLoadName (ALL_AXES);
        centralBox.RenderFilled();

        if (camera.type != Camera::RIGHT) {
            //x-axis
            glLoadName (X_AXIS);                            //Loads the name for the x-axis
            x_axisBoundingBox.RenderFilled();            
        }

        if (camera.type != Camera::TOP) {
            //y-axis           
            glLoadName (Y_AXIS);                            //Loads the name for the y-axis
            y_axisBoundingBox.RenderFilled();            
        }

        if (camera.type != Camera::FRONT) {
            //z-axis            
            glLoadName (Z_AXIS);                            //Loads the name for the z-axis
            z_axisBoundingBox.RenderFilled();            
        }
    glPopMatrix();
}

/// <summary>
/// Method called to set the x-axis as selected
/// </summary>
void CIView::FeedbackTranslation::SelectX() {
    x = true;
    y = false;
    z = false;
}

/// <summary>
/// Method called to set the y-axis as selected
/// </summary>
void CIView::FeedbackTranslation::SelectY() {
    x = false;
    y = true;
    z = false;
}

/// <summary>
/// Method called to set the z-axis as selected
/// </summary>
void CIView::FeedbackTranslation::SelectZ() {
    x = false;
    y = false;
    z = true;
}

/// <summary>
/// Method called to set all axes as selected
/// </summary>
void CIView::FeedbackTranslation::SelectAll() {
    x = true;
    y = true;
    z = true;
}

/// <summary>
/// Method called to set all axes as deselected
/// </summary>
void CIView::FeedbackTranslation::DeselectAll() {
    x = false;
    y = false;
    z = false;
}

/// <summary>
/// Method called to know if the x-axis is selected
/// </summary>
/// <returns> A boolean that indicates if the x-axis is selected </returns>
bool CIView::FeedbackTranslation::IsSelectedX() {
    return x;
}

/// <summary>
/// Method called to know if the y-axis is selected
/// </summary>
/// <returns> A boolean that indicates if the y-axis is selected </returns>
bool CIView::FeedbackTranslation::IsSelectedY() {
    return y;
}

/// <summary>
/// Method called to know if the z-axis is selected
/// </summary>
/// <returns> A boolean that indicates if the z-axis is selected </returns>
bool CIView::FeedbackTranslation::IsSelectedZ() {
    return z;
}

/// <summary>
/// Method called to lock the initial position during the 
/// translation operation, to show the "ghost" feedback
/// </summary>
void CIView::FeedbackTranslation::LockInitialPosition() {
    ghostPosition = position;
}

/// <summary>
/// Method called to apply a matrix transformation to the feedback position
/// </summary>
/// <param name="m"> The matrix to apply </param>
void CIView::FeedbackTranslation::ApplyMatrix (const Matrix& m) {
    position.Transform (m);
}
/***************************************************************************************************************
******************************************* endregion Utility Methods ******************************************
****************************************************************************************************************/
